//using UnityEngine;
using System;
using System.Collections;
using System.IO;
using System.Text;
using System.Threading;

namespace MxNetwork
{
    public class MxBuffer : IDisposable
    {
        public MxBuffer()
        {
        }
        
        public MxBuffer(int length)
        {		    
            m_data.Capacity = length;
	    }

        public MxBuffer(MxBuffer v)
        {
            m_data.Write(v.m_data.ToArray(), 0, (int)v.m_data.Length);
        }

        public void ResetRead()
	    {
		    m_dataReadOffset = 0;
	    }

        public void ResetWrite()
	    {
		    m_dataWriteOffset = 0;
	    }

        public void ResetReadWrite()
	    {
		    m_dataWriteOffset = 0;
		    m_dataReadOffset = 0;
	    }

        public int GetWriteOffset()
        {
            return m_dataWriteOffset;
        }

        public bool SetWriteOffset(int offset)
	    {
            if (offset > m_data.Length)
		    {
			    return false;
		    }
            m_dataWriteOffset = offset;
		    return true;
	    }

        public void SetWriteOffsetEnd()
        {           
            m_dataWriteOffset = (int)m_data.Length;           
        }   
		
		public void Dispose()
        {
            m_data.Close();
        }

        public bool MoveWriteOffset(int offset)
	    {
		    return SetWriteOffset(GetWriteOffset() + offset);
	    }

        public void MoveWriteOffsetEnd(int offset)
        {
            m_dataWriteOffset = (int)m_data.Length;
        }

        public int GetReadOffset()
        {
		    return m_dataReadOffset;
	    }

        public bool SetReadOffset(int offset)
	    {
		    if(offset > m_data.Length)
		    {
			    return false;
		    }

		    m_dataReadOffset = offset;
		    return true;
	    }

        public bool MoveReadOffset(int offset)
	    {
		    return SetReadOffset(GetReadOffset() + offset);
	    }

        public void MoveReadOffsetEnd(int offset)
        {
            m_dataReadOffset = (int)m_data.Length;
        }
	
	    public int GetCapacity()
	    {
		    return m_data.Capacity;
	    }

        public int GetAllDataSize()
        {
            return (int)m_data.Length;
        }
        
	    public void WriteData(byte[] pData)
	    {            
            m_data.Position = m_dataWriteOffset;
            m_data.Write(pData, 0, pData.Length);
            m_dataWriteOffset += pData.Length;
	    }

        public void WriteData(byte[] pData, int offset, int length)
        {
            m_data.Position = m_dataWriteOffset;
            m_data.Write(pData, offset, length);
            m_dataWriteOffset += length;
        }

        public void WriteData(byte[] pData, int length)
        {
            m_data.Position = m_dataWriteOffset;
            m_data.Write(pData, 0, length);
            m_dataWriteOffset += length;
        } 
     
        public sbyte ReadInt8()
        {            
            int dataLen = sizeof(sbyte);
            if ((m_dataReadOffset + dataLen) > m_data.Length)
            {
                return 0;
            }
            else
            {
                byte[] tmp = new byte[dataLen];
                m_data.Position = m_dataReadOffset;
                m_data.Read(tmp, 0, dataLen);
                m_dataReadOffset += dataLen;
                return (sbyte)tmp[0];                                
            }            
        }

        public byte ReadUInt8()
        {            
            int dataLen = sizeof(byte);
            if ((m_dataReadOffset + dataLen) > m_data.Length)
            {
                return 0;
            }
            else
            {                
                byte[] tmp = new byte[dataLen];
                m_data.Position = m_dataReadOffset;
                m_data.Read(tmp, 0, dataLen);
                m_dataReadOffset += dataLen;
                return tmp[0];                              
            }            
        }

        public short ReadInt16()
        {            
            int dataLen = sizeof(short);
            if ((m_dataReadOffset + dataLen) > m_data.Length)
            {
                return 0;
            }
            else
            {
                byte[] tmp = new byte[dataLen];
                m_data.Position = m_dataReadOffset;
                m_data.Read(tmp, 0, dataLen);
                m_dataReadOffset += dataLen;
                return BitConverter.ToInt16(tmp, 0);                               
            }            
        }

        public ushort ReadUInt16()
        {
            int dataLen = sizeof(ushort);
            if ((m_dataReadOffset + dataLen) > m_data.Length)
            {
                return 0;
            }
            else
            {
                byte[] tmp = new byte[dataLen];
                m_data.Position = m_dataReadOffset;
                m_data.Read(tmp, 0, dataLen);
                m_dataReadOffset += dataLen;
                return BitConverter.ToUInt16(tmp, 0);
            }            
        }

        public int ReadInt32()
        {
            int dataLen = sizeof(int);
            if ((m_dataReadOffset + dataLen) > m_data.Length)
            {
                return 0;
            }
            else
            {
                byte[] tmp = new byte[dataLen];
                m_data.Position = m_dataReadOffset;
                m_data.Read(tmp, 0, dataLen);
                m_dataReadOffset += dataLen;
                return BitConverter.ToInt32(tmp, 0);
            }            
        }

        public uint ReadUInt32()
        {
            int dataLen = sizeof(uint);
            if ((m_dataReadOffset + dataLen) > m_data.Length)
            {
                return 0;
            }
            else
            {
                byte[] tmp = new byte[dataLen];
                m_data.Position = m_dataReadOffset;
                m_data.Read(tmp, 0, dataLen);
                m_dataReadOffset += dataLen;
                return BitConverter.ToUInt32(tmp, 0);
            }           
        }

        public uint ReadUInt32ByPos(int Pos)
        {
            int dataLen = sizeof(uint);
            if (Pos> m_data.Length)
            {
                return 0;
            }
            else
            {
                byte[] tmp = new byte[dataLen];
                m_data.Position = Pos;
                m_data.Read(tmp, 0, dataLen);                
                return BitConverter.ToUInt32(tmp, 0);
            }
        }

        public long ReadInt64()
        {
            int dataLen = sizeof(long);
            if ((m_dataReadOffset + dataLen) > m_data.Length)
            {
                return 0;
            }
            else
            {
                byte[] tmp = new byte[dataLen];
                m_data.Position = m_dataReadOffset;
                m_data.Read(tmp, 0, dataLen);
                m_dataReadOffset += dataLen;
                return BitConverter.ToInt64(tmp, 0);
            }        
        }

        public ulong ReadUInt64()
        {
            int dataLen = sizeof(ulong);
            if ((m_dataReadOffset + dataLen) > m_data.Length)
            {
                return 0;
            }
            else
            {
                byte[] tmp = new byte[dataLen];
                m_data.Position = m_dataReadOffset;
                m_data.Read(tmp, 0, dataLen);
                m_dataReadOffset += dataLen;
                return BitConverter.ToUInt64(tmp, 0);
            }        
        }

        public float ReadFloat()
        {
            int dataLen = sizeof(float);
            if ((m_dataReadOffset + dataLen) > m_data.Length)
            {
                return 0;
            }
            else
            {
                byte[] tmp = new byte[dataLen];
                m_data.Position = m_dataReadOffset;
                m_data.Read(tmp, 0, dataLen);
                m_dataReadOffset += dataLen;
                return BitConverter.ToSingle(tmp, 0);
            }        
        }

        public double ReadDouble()
        {
            int dataLen = sizeof(double);
            if ((m_dataReadOffset + dataLen) > m_data.Length)
            {
                return 0;
            }
            else
            {
                byte[] tmp = new byte[dataLen];
                m_data.Position = m_dataReadOffset;
                m_data.Read(tmp, 0, dataLen);
                m_dataReadOffset += dataLen;
                return BitConverter.ToDouble(tmp, 0);
            }        
        }

        public string ReadString()
        {
            int dataLen = this.ReadInt32() * 2;
            if (dataLen == 0)
            {
                return "";
            }
            else
            {
                if ((m_dataReadOffset + dataLen) > m_data.Length)
                {
                    return "";
                }
                else
                {
                    byte[] tmp = new byte[dataLen-2];
                    m_data.Position = m_dataReadOffset;
                    m_data.Read(tmp, 0, (dataLen-2));
                    m_dataReadOffset += dataLen;
                    return System.Text.Encoding.Unicode.GetString(tmp);
                }        
            }            
        }

        public void Write(sbyte v)
        {
            byte[] tmp = new byte[1];
            tmp[0] = (byte)v;
            m_data.Position = m_dataWriteOffset;
            m_data.Write(tmp, 0, 1);
            m_dataWriteOffset += 1;
        }

        public void Write(byte v)
        {
            byte[] tmp = new byte[1];
            tmp[0] = v;
            m_data.Position = m_dataWriteOffset;
            m_data.Write(tmp, 0, 1);
            m_dataWriteOffset += 1;
        }

        public void Write(short v)
        {
            byte[] tmp = BitConverter.GetBytes(v);
            m_data.Position = m_dataWriteOffset;
            m_data.Write(tmp, 0, tmp.Length);
            m_dataWriteOffset += tmp.Length;
        }

        public void Write(ushort v)
        {
            byte[] tmp = BitConverter.GetBytes(v);
            m_data.Position = m_dataWriteOffset;
            m_data.Write(tmp, 0, tmp.Length);
            m_dataWriteOffset += tmp.Length;
        }

        public void Write(int v)
        {
            byte[] tmp = BitConverter.GetBytes(v);
            m_data.Position = m_dataWriteOffset;
            m_data.Write(tmp, 0, tmp.Length);
            m_dataWriteOffset += tmp.Length;
        }

        public void Write(uint v)
        {
            byte[] tmp = BitConverter.GetBytes(v);
            m_data.Position = m_dataWriteOffset;
            m_data.Write(tmp, 0, tmp.Length);
            m_dataWriteOffset += tmp.Length;
        }

        public void Write(long v)
        {
            byte[] tmp = BitConverter.GetBytes(v);
            m_data.Position = m_dataWriteOffset;
            m_data.Write(tmp, 0, tmp.Length);
            m_dataWriteOffset += tmp.Length;
        }

        public void Write(ulong v)
        {
            byte[] tmp = BitConverter.GetBytes(v);
            m_data.Position = m_dataWriteOffset;
            m_data.Write(tmp, 0, tmp.Length);
            m_dataWriteOffset += tmp.Length;
        }

        public void Write(float v)
        {
            byte[] tmp = BitConverter.GetBytes(v);
            m_data.Position = m_dataWriteOffset;
            m_data.Write(tmp, 0, tmp.Length);
            m_dataWriteOffset += tmp.Length;
        }

        public void Write(double v)
        {
            byte[] tmp = BitConverter.GetBytes(v);
            m_data.Position = m_dataWriteOffset;
            m_data.Write(tmp, 0, tmp.Length);
            m_dataWriteOffset += tmp.Length;
        }

        public void Write(string v)
        {            
            byte[] tmp = System.Text.Encoding.Unicode.GetBytes(v);
            Write((tmp.GetLength(0)/2) + 1);
            m_data.Position = m_dataWriteOffset;
            m_data.Write(tmp, 0, tmp.Length);
            m_data.WriteByte(0);
            m_data.WriteByte(0);
            m_dataWriteOffset += (tmp.Length + 2);
        }

        public byte[] GetData()
        {
            return m_data.GetBuffer();
        }
        
        protected MemoryStream m_data = new MemoryStream();
        protected int m_dataWriteOffset = 0;
	    protected int m_dataReadOffset = 0;
	    
    }

    public class MxNwkPacket : MxBuffer
    {
        public enum MxNwkPacketFlag
        {
            NPF_HAS_ID = 1,
            NPF_HAS_ORDER = 2,
            NPF_HAS_TS = 4,
            NPF_IS_ENCRYPT = 8,
            NPF_MAX
        };

        public enum MxNwkPacketType
        {
            NPT_DEFAULT = 0,
            NPT_MESSAGE = 1,
            NPT_REQUEST = 2,
            NPT_ACK = 3,
            NPT_MAX = 4
        };
        
        public MxNwkPacket()
        {

        }

        public MxNwkPacket(MxNwkPacketType packetType)
        {
            
            switch(packetType)
            {
                case MxNwkPacketType.NPT_MESSAGE:
                    {
                        byte[] tmp = new byte[_HeadSizeForMsg];
                        for (int i = 0; i < _HeadSizeForMsg; ++i)
                        {
                            tmp[i] = 0;
                        }
                        WriteData(tmp);
                    }
                    break;
                case MxNwkPacketType.NPT_REQUEST:
                    {
                        byte[] tmp = new byte[_HeadSize];
                        for (int i = 0; i < _HeadSize; ++i)
                        {
                            tmp[i] = 0;
                        }
                        WriteData(tmp);
                    }
                    break;
                case MxNwkPacketType.NPT_ACK:
                    {
                        byte[] tmp = new byte[_HeadSize];
                        for (int i = 0; i < _HeadSize; ++i)
                        {
                            tmp[i] = 0;
                        }
                        WriteData(tmp);
                    }
                    break;
            }

            SetPacketType((byte)packetType);
        }

        public uint GetPacketFlag()
        {
            int dataLen = sizeof(uint);            
            byte[] tmp = new byte[dataLen];
            m_data.Position = _FlagPos;
            m_data.Read(tmp, 0, dataLen);            
            return BitConverter.ToUInt32(tmp, 0);                      
        }

        public uint GetPacketID()
        {
            int dataLen = sizeof(uint);
            byte[] tmp = new byte[dataLen];
            m_data.Position = _IDPos;
            m_data.Read(tmp, 0, dataLen);
            return BitConverter.ToUInt32(tmp, 0);   
        }

        public void SetPacketID(uint v)
        {
            byte[] tmp = BitConverter.GetBytes(v);
            m_data.Position = _IDPos;
            m_data.Write(tmp, 0, tmp.Length);
        }
                
        public byte GetPacketType()
        {        
            byte[] tmp = new byte[1];
            m_data.Position = _FlagPos + 3;
            m_data.Read(tmp, 0, 1);
            return tmp[0]; 
        }

        public void SetPacketType(byte v)
        {
            byte[] tmp = new byte[1];
            tmp[0] = v;
            m_data.Position = _FlagPos + 3;
            m_data.Write(tmp, 0, tmp.Length);
        }
	    
        public int GetPacketUserID()
        {
            int dataLen = sizeof(int);
            byte[] tmp = new byte[dataLen];
            m_data.Position = _UserIDPos;
            m_data.Read(tmp, 0, dataLen);
            return BitConverter.ToInt32(tmp, 0); 
        }

        public void SetPacketUserID(int v)
        {
            byte[] tmp = BitConverter.GetBytes(v);
            m_data.Position = _UserIDPos;
            m_data.Write(tmp, 0, tmp.Length);
        }
	    
        public int GetPacketCharacterID()
        {
            int dataLen = sizeof(int);
            byte[] tmp = new byte[dataLen];
            m_data.Position = _CharacterIDPos;
            m_data.Read(tmp, 0, dataLen);
            return BitConverter.ToInt32(tmp, 0); 
        }

        public void SetPacketCharacterID(int v)
        {
            byte[] tmp = BitConverter.GetBytes(v);
            m_data.Position = _CharacterIDPos;
            m_data.Write(tmp, 0, tmp.Length);
        }
	    
        public int GetPacketToken()
        {
            if ((MxNwkPacket.MxNwkPacketType)GetPacketType() == MxNwkPacketType.NPT_MESSAGE)
            {
                return -1;
            }
            else
            {
                int dataLen = sizeof(int);
                byte[] tmp = new byte[dataLen];
                m_data.Position = _TokenPos;
                m_data.Read(tmp, 0, dataLen);
                return BitConverter.ToInt32(tmp, 0); 
            }            
        }

        public void SetPacketToken(int v)
        {
            if ((MxNwkPacket.MxNwkPacketType)GetPacketType() != MxNwkPacketType.NPT_MESSAGE)
            {
                byte[] tmp = BitConverter.GetBytes(v);
                m_data.Position = _TokenPos;
                m_data.Write(tmp, 0, tmp.Length);
            }            
        }
	    
        public int GetPacketChannelID()
        {
            return ChannelID; 
        }

        public void SetPacketChannelID(int v)
        {
            ChannelID = v;
        }
	    
        public uint GetPacketResult()
        {
            if ((MxNwkPacket.MxNwkPacketType)GetPacketType() == MxNwkPacketType.NPT_MESSAGE)
            {
                return 0;
            }
            else
            {
                int dataLen = sizeof(uint);
                byte[] tmp = new byte[dataLen];
                m_data.Position = _ResultPos;
                m_data.Read(tmp, 0, dataLen);
                return BitConverter.ToUInt32(tmp, 0); 
            }            
        }

        public void SetPacketResult(uint v)
        {
            if ((MxNwkPacket.MxNwkPacketType)GetPacketType() != MxNwkPacketType.NPT_MESSAGE)
            {
                byte[] tmp = BitConverter.GetBytes(v);
                m_data.Position = _ResultPos;
                m_data.Write(tmp, 0, tmp.Length);
            }            
        }

        public void SetPacketEncryptFlag(bool v)
        {
            uint flag = GetPacketFlag();
            if(v == true)
            {
                flag = flag | 0x00000008;
            }
            else
            {
                flag = flag & 0xfffffff7;
            }
            SetPacketFlag(flag);
        }

        public bool GetPacketEncryptFlag()
        {
            uint flag = GetPacketFlag();

	        if((flag & 0x00000008) > 0)
	        {
		        return true;
	        }
	        else
	        {
		        return false;
	        }
        }

        public void SetRemoteType(uint v)
        {
            RemoteType = v;
        }

        public uint GetRemoteType()
        {
            return RemoteType;
        }

        public void SetRemoteID(uint v)
        {
            RemoteID = v;
        }

        public uint GetRemoteID()
        {
            return RemoteID;
        }

        public void SetRemoteIP(MxNwkAddressIP v)
        {
            RemoteIP = v;
        }

        public MxNwkAddressIP GetRemoteIP()
        {
            return RemoteIP;
        }

        public void SetPacketRealSize()
        {
            byte[] tmp = BitConverter.GetBytes(GetRealSize());
            m_data.Position = 0;
            m_data.Write(tmp, 0, tmp.Length);
        }

        public uint GetPacketRealSize()
        {
            int dataLen = sizeof(uint);
            byte[] tmp = new byte[dataLen];
            m_data.Position = 0;
            m_data.Read(tmp, 0, dataLen);
            return BitConverter.ToUInt32(tmp, 0);
        }
        
        public uint GetRealSize()
        {
            return (uint)m_data.Length;
        }

        public uint GetRealDataSize()
        {
            return (uint)m_data.Length - GetDataPos();
        }        

        public uint GetDataPos()
        {
            switch ((MxNwkPacket.MxNwkPacketType)GetPacketType())
	        {
                case MxNwkPacketType.NPT_MESSAGE:
		            { 
			            return _DataPosForMsg;	
		            }                    
                case MxNwkPacketType.NPT_REQUEST:
                    {
                        return _DataPos;
                    }                    
                case MxNwkPacketType.NPT_ACK:
		            {
			            return _DataPos;	
		            }                    
	            default:
		            return 0;
	        }
        }        
        
        public const int _HeadSize = sizeof(uint) * 3 + sizeof(int) * 3 + sizeof(uint);
        public const int _HeadSizeForMsg = sizeof(uint) * 3 + sizeof(int) * 2;
        public const int _FlagPos = sizeof(uint);
        public const int _IDPos = sizeof(uint) * 2;                
        public const int _UserIDPos = sizeof(uint) * 3;
        public const int _CharacterIDPos = sizeof(uint) * 3 + sizeof(int);
        public const int _TokenPos = sizeof(uint) * 3 + sizeof(int) * 2;        
        public const int _ResultPos = sizeof(uint) * 3 + sizeof(int) * 3;
        public const int _DataPos = sizeof(uint) * 3 + sizeof(int) * 3 + sizeof(uint);
        public const int _DataPosForMsg = sizeof(uint) * 3 + sizeof(int) * 2;

        private void SetPacketFlag(uint v)
        {
            byte[] tmp = BitConverter.GetBytes(v);
            m_data.Position = _FlagPos;
            m_data.Write(tmp, 0, tmp.Length);           
        }
                
        private MxNwkAddressIP RemoteIP;
        private uint RemoteType = 0;
        private uint RemoteID = 0;
        private int ChannelID = 0;

        public AutoResetEvent ReqEvent;
    }
}
